home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Tool Chest / Development Tools & Languages / Macintosh Common Lisp Related / Drag and Drop / drag-and-drop.lisp < prev    next >
Encoding:
Text File  |  1994-09-12  |  43.8 KB  |  1,044 lines  |  [TEXT/CCL2]

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;;;
  3. ;;;; Macintosh Drag and Drop for MCL
  4. ;;;;
  5. ;;;; Almost, but quite all of the Macintosh Drag Manager is implemented here.
  6. ;;;; There are a couple of bonuses, though, the biggest being that the
  7. ;;;; implementation easily supports drag and drop behavior among views in a
  8. ;;;; window.
  9. ;;;;
  10. ;;;; General usage notes:
  11. ;;;;
  12. ;;;; ¥ In order to drag items from a view or receive drops the view's window
  13. ;;;;   must include 'drag-&-drop-window-mixin class its definition.
  14. ;;;; ¥ If you want a particular view to be able to receive a drop, make sure
  15. ;;;;   you've specialized the 'drag-receive-drag method.
  16. ;;;; ¥ If you want be able to drag items out of a particular view, specialize
  17. ;;;;   the 'drag-selection-p method.
  18. ;;;; ¥ Events from the Drag Manager are piped through a dispatching function
  19. ;;;;   and sent to MCL methods, which you should specialize.  The methods
  20. ;;;;   are all named 'drag-tracking-xxxx.
  21. ;;;;
  22. ;;;; Following the Real Code are a couple of examples.  The first is a subclass
  23. ;;;; of fred, allowing you to drag selections out as either text data (to other
  24. ;;;; text-oriented applications), text clipping files in the Finder or as MCL
  25. ;;;; files in the Finder.  The second example is a simple view with two text
  26. ;;;; fields that allow dragging and dropping between them.  Both examples allow
  27. ;;;; you to drop text data, text clipping files and text documents into their
  28. ;;;; respective drop locations.  After evaluating the commented code you can
  29. ;;;; bring up the examples via (fred-example) and (text-item-example) in the
  30. ;;;; Listener.
  31. ;;;;
  32. ;;;; Please let me know if you have any suggestions, comments or bug reports!
  33. ;;;;
  34. ;;;; Dan S. Camper
  35. ;;;; camper@applelink.apple.com
  36. ;;;; 8/24/94
  37. ;;;;
  38.  
  39. (in-package :ccl)
  40.  
  41. (require :quickdraw)
  42.  
  43. (export '(drag-&-drop-window-mixin
  44.           drag-reference drag-region tracking-handler receive-handler
  45.           drag-window-p
  46.           
  47.           drag-tracking-enter-handler drag-tracking-leave-handler
  48.           drag-tracking-enter-view drag-tracking-leave-view
  49.           drag-tracking-in-view
  50.           
  51.           drag-manager-present-p
  52.           
  53.           drag-attributes
  54.           drag-within-sender-application-p
  55.           drag-left-sender-window-p drag-left-sender-view-p
  56.           drag-within-sender-window-p drag-within-sender-view-p
  57.           
  58.           drag-item-count
  59.           drag-item-reference-number
  60.           drag-item-flavor-count
  61.           drag-item-flavor-type drag-item-flavor-type-list
  62.           drag-item-flavor-flags
  63.           drag-item-flavor-size
  64.           drag-item-flavor-data with-drag-item-flavor-data
  65.           drag-item-flavor-exists-p
  66.           with-drag-items
  67.           
  68.           with-new-drag
  69.           drag-add-item-flavor
  70.           drag-set-item-flavor-data
  71.           drag-promise-item-flavor
  72.           drag-add-hfs-flavor
  73.           drag-promise-hfs-flavor
  74.           drag-begin-drag
  75.           drag-send-data
  76.           drag-selection-p
  77.           $PromisedHFSObject
  78.           
  79.           drag-receive-drag
  80.           drag-get-drop-location drag-get-drop-location-as-path
  81.           
  82.           view-drag-hilite
  83.           find-view-containing-global-point
  84.           drag-mouse-location drag-mouse-view
  85.           create-drag-item-bounds
  86.           wptr-to-drag-window drag-window-to-wptr drag-reference-to-window))
  87.  
  88. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  89. ;;;
  90. ;;; Low-level stuff....
  91. ;;;
  92.  
  93. (defvar *drag-&-drop-window-list* nil)
  94. (defconstant $PromisedHFSObject :|HFSp|)
  95.  
  96. (defmacro oserr-check (&body body)
  97.   (let ((result (gensym)))
  98.     `(let ((,result (progn ,@body)))
  99.        (cond ((eql ,result #$userCanceledErr)
  100.               (throw-cancel ,result))
  101.              ((neq ,result #$noErr)
  102.               (error "~S" ,result)))
  103.        ,result)))
  104.  
  105. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  106. ;;;
  107. ;;; Patch to MCL event system
  108. ;;;
  109. ;;; A hook into MCL's mouseDown handling.  All drags initiated from MCL pass
  110. ;;; through here first.
  111. ;;;
  112. (let ((*warn-if-redefine-kernel* nil))
  113.   
  114.   (defun process-multi-clicks (event)
  115.     ;called by event-dispatch on mouse-down events
  116.     (rlet ((wptr :WindowPtr))
  117.       (let ((the-part (#_FindWindow (rref event eventrecord.where) wptr))
  118.             (view nil)
  119.             (window nil))
  120.         (if (and (= the-part #$inContent)
  121.                  (setf window (wptr-to-drag-window (%get-ptr wptr)))
  122.                  (setf view (find-view-containing-global-point window (rref event eventrecord.where)))
  123.                  (drag-selection-p view (rref event eventrecord.where))
  124.                  (#_WaitMouseMoved (rref event eventrecord.where)))
  125.           (unwind-protect
  126.             (progn
  127.               (setf (%source-view window) view)
  128.               (drag-begin-drag view event))
  129.             (setf (%source-view window) nil))
  130.           ; one of the above tests failed; do the usual thing
  131.           (if (and (%i< (%i- (rref event eventrecord.when) *last-mouse-down-time*)
  132.                         (%get-long (%int-to-ptr #$DoubleTime)))
  133.                    (double-click-spacing-p *last-mouse-down-position*
  134.                                            (rref event eventrecord.where)))
  135.             (incf *multi-click-count*)
  136.             (setf *last-mouse-down-position* (rref event eventrecord.where)
  137.                   *multi-click-count* 1)))))
  138.     (setq *last-mouse-down-time* (rref event eventrecord.when)))
  139.  
  140.   )
  141.  
  142. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  143. ;;;
  144. ;;; ¥¥¥ Low-level entries to/from Drag Manager
  145. ;;;
  146. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  147.  
  148. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  149. ;;
  150. ;; The system calls this function whenever a dragged item causes an event in a
  151. ;; drag & drop window.  Following is the default version; a different routine can
  152. ;; be specified with :tracking-handler argument during the window's make-instance.
  153. ;;
  154. ;; This default version acts as a dispatcher to predefined MCL methods, which you
  155. ;; should specialize as necessary.  The methods to specialize are:
  156. ;;
  157. ;;    drag-tracking-enter-handler
  158. ;;    drag-tracking-leave-handler
  159. ;;    drag-tracking-enter-view
  160. ;;    drag-tracking-leave-view
  161. ;;    drag-tracking-in-view
  162. ;;
  163. ;; This spoofs the normal Drag Manager behavior quite a bit.  Basically, we're
  164. ;; allowing window-like drag behavior from subviews in the window.
  165. ;;
  166. (defpascal DragTrackingHandlerDispatch.p (:word $theMessage :ptr $theWindow :ptr $handlerRefCon
  167.                                                 :long $dragReference
  168.                                                 :word)
  169.   (declare (ignore $handlerRefcon))
  170.   (let ((w (wptr-to-drag-window $theWindow))
  171.         (old-ref 0))
  172.     (when w
  173.       (unwind-protect
  174.         (progn
  175.           (setf old-ref (drag-reference w)      ; save old reference and restore when completed
  176.                 (slot-value w 'drag-reference) $dragReference)
  177.           (case $theMessage
  178.             (#.#$dragTrackingEnterHandler
  179.              (drag-tracking-enter-handler w))
  180.             (#.#$dragTrackingEnterWindow
  181.              (let ((v (drag-mouse-view w)))
  182.                (drag-tracking-enter-view v)
  183.                (setf (%last-view w) v)))
  184.             (#.#$dragTrackingInWindow
  185.              (let ((v (drag-mouse-view w)))
  186.                (unless (equal (%last-view w) v)   ; see if we're switching views and adjust if necessary
  187.                  (when (%last-view w)
  188.                    (drag-tracking-leave-view (%last-view w)))
  189.                  (when v
  190.                    (drag-tracking-enter-view v)))
  191.                (drag-tracking-in-view v)
  192.                (setf (%last-view w) v)))
  193.             (#.#$dragTrackingLeaveWindow
  194.              (let ((v (drag-mouse-view w)))
  195.                (drag-tracking-leave-view v))
  196.              (setf (%last-view w) nil))
  197.             (#.#$dragTrackingLeaveHandler
  198.              (drag-tracking-leave-handler w))))
  199.         (setf (slot-value w 'drag-reference) old-ref))))
  200.   #$noErr)
  201.  
  202. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  203. ;;
  204. ;; The system calls this function when an item or items are being dropped into a
  205. ;; drag & drop window.  Following is the default version; a different routine can
  206. ;; be specified with :receive-handler argument during the window's make-instance.
  207. ;;
  208. ;; Specialize the 'drag-receive-drag function for your view in order to receive
  209. ;; drops from the Drag Manager.
  210. ;;
  211. (defpascal DragReceiveHandlerDispatch.p (:ptr $theWindow :ptr $handlerRefCon :long $dragReference
  212.                                               :word)
  213.   (declare (ignore $handlerRefcon))
  214.   (let ((w (wptr-to-drag-window $theWindow))
  215.         (old-ref 0)
  216.         (result #$dragNotAcceptedErr))
  217.     (when w
  218.       (unwind-protect
  219.         (progn
  220.           (setf old-ref (drag-reference w)      ; save old reference and restore when completed
  221.                 (slot-value w 'drag-reference) $dragReference
  222.                 result (drag-receive-drag (drag-mouse-view w))))
  223.         (setf (slot-value w 'drag-reference) old-ref)))
  224.     (cond ((eql result t) (setf result #$noErr))
  225.           ((not (integerp result)) (setf result #$dragNotAcceptedErr)))
  226.     result))
  227.  
  228. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  229. ;;
  230. ;; This function is called when the destination requests item data that was not cached.
  231. ;; It's basically a dispatch routine; specialize the 'drag-send-data method to
  232. ;; implement.
  233. ;;
  234. ;; The actual definition of this callback cites a long (actually a parameter of type
  235. ;; 'flavorType' which is an OSType which is four bytes) but there seems to be a bug in
  236. ;; the defpascal macro (or something).  If you specify a long there then the four high
  237. ;; bits are always set.  Defining it as a pointer and then extracting it with %ptr-to-int
  238. ;; seems to work.
  239. ;;
  240. (defpascal DragSendDataProc.p (:ptr $flavorType :ptr $handlerRefCon :long $itemReference
  241.                                      :long $dragReference
  242.                                      :word)
  243.   (declare (ignore $handlerRefcon))
  244.   (let ((w (drag-reference-to-window $dragReference))
  245.         (flavor-type nil)
  246.         (result #$noErr))
  247.     (when w
  248.       (rlet ((temp :longint))
  249.         (%put-long temp (%ptr-to-int $flavorType))
  250.         (setf flavor-type (%get-ostype temp))
  251.         (setf result (drag-send-data (%source-view w) $itemReference flavor-type))
  252.         (cond ((eql result t) (setf result #$noErr))
  253.               ((not (integerp result)) (setf result #$badDragFlavorErr)))))
  254.     result))
  255.  
  256. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  257. ;;;
  258. ;;; ¥¥¥ Class Definitions for drag & drop windows
  259. ;;;
  260. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  261.  
  262. (defclass drag-&-drop-window-mixin ()
  263.   ((drag-reference :initform 0 :reader drag-reference)
  264.    (tracking-handler :initform DragTrackingHandlerDispatch.p :initarg :tracking-handler :reader tracking-handler)
  265.    (receive-handler :initform DragReceiveHandlerDispatch.p :initarg :receive-handler :reader receive-handler)
  266.    (%last-view :initform nil :accessor %last-view)
  267.    (%source-view :initform nil :accessor %source-view)
  268.    (drag-region :initform nil :accessor drag-region)
  269.    ))
  270.  
  271. (defmethod initialize-instance :after ((w drag-&-drop-window-mixin) &rest initargs)
  272.   (declare (ignore initargs))
  273.   (push (cons (wptr w) w) *drag-&-drop-window-list*)
  274.   (oserr-check (#_InstallTrackingHandler (tracking-handler w) (wptr w) (%null-ptr)))
  275.   (oserr-check (#_InstallReceiveHandler (receive-handler w) (wptr w) (%null-ptr)))
  276.   )
  277.  
  278. (defmethod window-close :before ((w drag-&-drop-window-mixin))
  279.   ; Cleanup the window's internal data
  280.   (setf *drag-&-drop-window-list* (delete (wptr w) *drag-&-drop-window-list* :key #'car))
  281.   (when (drag-region w)
  282.     (dispose-region (drag-region w))
  283.     (setf (drag-region w) nil))
  284.   (ignore-errors (#_DisposeDrag (drag-reference w)))
  285.   (when (tracking-handler w)
  286.     (oserr-check (#_RemoveTrackingHandler (tracking-handler w) (wptr w)))
  287.     (setf (slot-value w 'tracking-handler) nil))
  288.   (when (receive-handler w)
  289.     (oserr-check (#_RemoveReceiveHandler (receive-handler w) (wptr w)))
  290.     (setf (slot-value w 'receive-handler) nil)))
  291.  
  292. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  293. ;;
  294. ;; ¥¥¥ The following methods should be shadowed by the user to get actual
  295. ;; ¥¥¥ behavior.  Note that some functions should be specialized on the window
  296. ;; ¥¥¥ while others can go down to a view.  The argument name is an indication
  297. ;; ¥¥¥ of which is which.
  298. ;;
  299. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  300.  
  301. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  302. ;;
  303. ;; Called whenever a particular drag handler is called for the first time.
  304. ;;
  305. (defmethod drag-tracking-enter-handler ((window t))
  306.   (declare (ignore window))
  307.   nil)
  308.  
  309. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  310. ;;
  311. ;; Called whenever a drag enters a drag & drop window or a view within the
  312. ;; window.
  313. ;;
  314. (defmethod drag-tracking-enter-view ((view t))
  315.   (declare (ignore view))
  316.   nil)
  317.  
  318. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  319. ;;
  320. ;; Called while a drag is occuring within a view within a drag & drop window.
  321. ;;
  322. (defmethod drag-tracking-in-view ((view t))
  323.   (declare (ignore view))
  324.   nil)
  325.  
  326. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  327. ;;
  328. ;; Called whenever a drag leaves a drag & drop window or a subview.
  329. ;;
  330. (defmethod drag-tracking-leave-view ((view t))
  331.   (declare (ignore view))
  332.   nil)
  333.  
  334. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  335. ;;
  336. ;; Called whenever a drag is exiting a particular drag handler.
  337. ;;
  338. (defmethod drag-tracking-leave-handler ((window t))
  339.   (declare (ignore window))
  340.   nil)
  341.  
  342. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  343. ;;
  344. ;; Called when the Drag Manager is dropping something into a drag & drop window
  345. ;; or subview.
  346. ;;
  347. (defmethod drag-receive-drag ((view t))
  348.   (declare (ignore view))
  349.   nil)
  350.  
  351. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  352. ;;
  353. ;; Boolean indicating whether it's appropriate to begin a drag or not.
  354. ;; Should take into account such things as hilited items, where the mouse
  355. ;; is, etc..  Note that the mouse position is in global coordinates.
  356. ;;
  357. (defmethod drag-selection-p ((view t) global-mouse-position)
  358.   (declare (ignore view global-mouse-position))
  359.   nil)
  360.  
  361. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  362. ;;
  363. ;; Called at the start of a drag from a view within a drag & drop window.
  364. ;; EventRecord is a the standard Macintosh event record that is currently
  365. ;; being processed.
  366. ;;
  367. (defmethod drag-begin-drag ((view t) eventrecord)
  368.   (declare (ignore view eventrecord))
  369.   nil)
  370.  
  371. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  372. ;;
  373. ;; Called when destination needs item data that was not cached.  The view
  374. ;; argument will be the view the drag initiated from.  Note that the 'flavor'
  375. ;; argument is an OSType.
  376. ;;
  377. (defmethod drag-send-data ((view t) (item-reference integer) (flavor keyword))
  378.   (declare (ignore view item-reference flavor))
  379.   nil)
  380.  
  381. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  382. ;;;
  383. ;;; ¥¥¥ Drag Toolkit
  384. ;;;
  385. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  386.  
  387. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  388. ;;
  389. ;; Boolean indicating the presence of the Drag Manager in the current system.
  390. ;;
  391. (defun drag-manager-present-p ()
  392.   (logbitp #$gestaltDragMgrPresent (gestalt #$gestaltDragMgrAttr)))
  393.  
  394. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  395. ;;
  396. ;; Provides the Drag Manager default hiliting of destination windows (drawing
  397. ;; the gray border around the inside of the window's edge).  Spoofed to
  398. ;; automatically perform this behavior on the view.
  399. ;;
  400. (defmethod view-drag-hilite ((view simple-view) hilite-p &optional (topleft nil) (bottomright nil))
  401.   (let ((window (view-window view))
  402.         (result nil))
  403.     (if hilite-p
  404.       (let ((region (new-region)))
  405.         (multiple-value-bind (topcorner bottomcorner) (view-corners view)
  406.           (unless (integerp topleft)
  407.             (setf topleft topcorner))
  408.           (unless (integerp bottomright)
  409.             (setf bottomright bottomcorner)))
  410.         (with-focused-view window
  411.           (oserr-check (#_ShowDragHilite (drag-reference window) (set-rect-region region topleft bottomright) t)))
  412.         (dispose-region region)
  413.         (setf result t))
  414.       (progn
  415.         (with-focused-view window
  416.           (oserr-check (#_HideDragHilite (drag-reference window))))))
  417.     result))
  418.  
  419. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  420. ;;
  421. ;; Testing to see if an MCL object or WindowRecord pointer can accept drags
  422. ;;
  423. (defmethod drag-window-p ((window-or-ptr macptr))
  424.   (if (wptr-to-drag-window window-or-ptr)
  425.     t))
  426.  
  427. (defmethod drag-window-p ((window-or-ptr drag-&-drop-window-mixin))
  428.   t)
  429.  
  430. (defmethod drag-window-p ((window-or-ptr t))
  431.   nil)
  432.  
  433. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  434. ;;
  435. ;; This hopefully makes the process of starting a new drag a bit easier.  The
  436. ;; setup-form argument should be a single Lisp form to execute before the Drag Manager's
  437. ;; #_TrackDrag routine is called; cleanup-form is optional but, if included,
  438. ;; should also be a single Lisp form that is called after the tracking is complete.
  439. ;; Both forms can assume the presence of a valid Drag Reference and associated
  440. ;; information.
  441. ;;
  442. ;; As an added bonus this macro always installs the DragSendDataProc.p callback
  443. ;; routine, which serves as a dispatcher to the 'drag-send-data MCL method.
  444. ;; 
  445. (defmacro with-new-drag (window eventrecord setup-form &optional (cleanup-form nil))
  446.   (let (($DragRef (gensym))
  447.         (setup-result (gensym))
  448.         (cleanup-result (gensym)))
  449.     `(let ((,setup-result nil)
  450.            (,cleanup-result nil))
  451.        (rlet ((,$DragRef :DragReference))
  452.          (oserr-check (#_NewDrag ,$DragRef))
  453.          (setf (slot-value ,window 'drag-reference) (%get-long ,$DragRef)
  454.                (drag-region ,window) (new-region))
  455.          (unwind-protect
  456.            (progn
  457.              (oserr-check (#_SetDragSendProc (drag-reference ,window) DragSendDataProc.p (%null-ptr)))
  458.              (setf ,setup-result (progn ,setup-form))
  459.              (oserr-check (#_TrackDrag (drag-reference ,window) ,eventrecord (drag-region ,window)))
  460.              (if ,cleanup-form
  461.                (setf ,cleanup-result (progn ,cleanup-form))))
  462.            (progn
  463.              (dispose-region (drag-region ,window))
  464.              (setf (drag-region ,window) nil)
  465.              (oserr-check (#_DisposeDrag (drag-reference ,window)))
  466.              (setf (slot-value ,window 'drag-reference) 0))))
  467.        (values ,setup-result ,cleanup-result))))
  468.  
  469. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  470. ;;
  471. ;; Conversion routines
  472. ;;
  473. (defmethod wptr-to-drag-window ((window-ptr macptr))
  474.   (cdr (find window-ptr *drag-&-drop-window-list* :key #'car)))
  475.  
  476. (defmethod drag-window-to-wptr ((window drag-&-drop-window-mixin))
  477.   (wptr window))
  478.  
  479. (defmethod drag-reference-to-window ((reference integer))
  480.   (cdr (find reference *drag-&-drop-window-list* :key #'(lambda (x) (drag-reference (cdr x))))))
  481.  
  482. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  483. ;;
  484. ;; Obtaining the attributes of a particular drag.  General first, then
  485. ;; specific predicates.
  486. ;;
  487. (defmethod drag-attributes ((window drag-&-drop-window-mixin))
  488.   (rlet ((flags :DragAttributes))
  489.     (oserr-check (#_GetDragAttributes (drag-reference window) flags))
  490.     (%get-long flags)))
  491.  
  492. (defmethod drag-left-sender-window-p ((window drag-&-drop-window-mixin))
  493.   (and (drag-window-p window)
  494.        (logtest #$dragHasLeftSenderWindow (drag-attributes window))))
  495.  
  496. (defmethod drag-left-sender-view-p ((view simple-view))
  497.   (let ((window (view-window view)))
  498.     (and (drag-window-p window)
  499.          (or (drag-left-sender-window-p window)
  500.              (not (equal (%source-view window) (drag-mouse-view window)))))))
  501.  
  502. (defmethod drag-within-sender-application-p ((window drag-&-drop-window-mixin))
  503.   (and (drag-window-p window)
  504.        (logtest #$dragInsideSenderApplication (drag-attributes window))))
  505.  
  506. (defmethod drag-within-sender-window-p ((window drag-&-drop-window-mixin))
  507.   (and (drag-window-p window)
  508.        (logtest #$dragInsideSenderWindow (drag-attributes window))))
  509.  
  510. (defmethod drag-within-sender-view-p ((view simple-view))
  511.   (let ((window (view-window view)))
  512.     (and (drag-within-sender-window-p window)
  513.          (equal (%source-view window) (drag-mouse-view window)))))
  514.  
  515. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  516. ;;
  517. ;; Returns the number of items in a drag
  518. ;;
  519. (defmethod drag-item-count ((window drag-&-drop-window-mixin))
  520.   (rlet ((count :signed-integer))
  521.     (oserr-check (#_CountDragItems (drag-reference window) count))
  522.     (%get-word count)))
  523.  
  524. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  525. ;;
  526. ;; Returns the drag item reference number of the nth item.
  527. ;;
  528. (defmethod drag-item-reference-number ((window drag-&-drop-window-mixin) &optional (index 1))
  529.   (rlet ((item :ItemReference))
  530.     (oserr-check (#_GetDragItemReferenceNumber (drag-reference window) index item))
  531.     (%get-long item)))
  532.  
  533. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  534. ;;
  535. ;; Returns the number of flavors in a given drag item.
  536. ;;
  537. (defmethod drag-item-flavor-count ((window drag-&-drop-window-mixin) (item-reference integer))
  538.   (rlet ((count :signed-integer))
  539.     (let ((err (#_CountDragItemFlavors (drag-reference window) item-reference count)))
  540.       (if (eql err #$noErr)
  541.         (%get-word count)
  542.         0))))
  543.  
  544. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  545. ;;
  546. ;; Obtains the flavor for a given item.
  547. ;;
  548. (defmethod drag-item-flavor-type ((window drag-&-drop-window-mixin) (item-reference integer)
  549.                                   &optional (flavor-index 1))
  550.   (rlet ((flavor :ostype))
  551.     (let ((err (#_GetFlavorType (drag-reference window) item-reference flavor-index flavor)))
  552.       (if (eql err #$noErr)
  553.         (%get-ostype flavor)))))
  554.  
  555. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  556. ;;
  557. ;; Build a list of flavor types for the given drag item.  The position of types in
  558. ;; the returned list is the same as the relative ordering in the actual drag item.
  559. ;;
  560. (defmethod drag-item-flavor-type-list ((w drag-&-drop-window-mixin) (item-reference integer))
  561.   (let ((flavor-list nil)
  562.         (count (drag-item-flavor-count w item-reference)))
  563.     (dotimes (index count)
  564.       (push (drag-item-flavor-type w item-reference (1+ index)) flavor-list))
  565.     (reverse flavor-list)))
  566.  
  567. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  568. ;;
  569. ;; Obtains the flavor flags for a given item.  Note that the 'flavor' argument is
  570. ;; an OSType.
  571. ;;
  572. (defmethod drag-item-flavor-flags ((window drag-&-drop-window-mixin) (flavor keyword)
  573.                                    (item-reference integer))
  574.   (rlet ((flags :FlavorFlags))
  575.     (let ((err (#_GetFlavorFlags (drag-reference window) item-reference flavor flags)))
  576.       (if (eql err #$noErr)
  577.         (%get-long flags)))))
  578.  
  579. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  580. ;;
  581. ;; Boolean indicating whether a particular flavor in an item is available.  Note
  582. ;; that the 'flavor' argument is an OSType.
  583. ;;
  584. (defmethod drag-item-flavor-exists-p ((window drag-&-drop-window-mixin) (flavor keyword)
  585.                                       (item-reference integer))
  586.   (if (drag-item-flavor-flags window flavor item-reference)
  587.     t))
  588.  
  589. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  590. ;;
  591. ;; Obtains the size of a particular item's flavor.  Note that the 'flavor' argument
  592. ;; is an OSType.
  593. ;;
  594. (defmethod drag-item-flavor-size ((window drag-&-drop-window-mixin) (flavor keyword)
  595.                                   (item-reference integer))
  596.   (rlet ((size :size))
  597.     (oserr-check (#_GetFlavorDataSize (drag-reference window) item-reference flavor size))
  598.     (%get-long size)))
  599.  
  600. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  601. ;;
  602. ;; Obtains the data for a particular item's flavor.  Note that the 'flavor' argument
  603. ;; is an OSType.  Also note that if the function succeeds (ie, it returns a pointer)
  604. ;; then you MUST eventually dispose of the pointer!
  605. ;;
  606. (defmethod drag-item-flavor-data ((window drag-&-drop-window-mixin) (flavor keyword)
  607.                                   (item-reference integer))
  608.   (let ((size (drag-item-flavor-size window flavor item-reference)))
  609.     (rlet ((size-ptr :integer))
  610.       (when (and (numberp size) (plusp size))
  611.         (%put-word size-ptr size)
  612.         (let ((data-ptr (#_NewPtr size)))
  613.           (oserr-check (#_GetFlavorData (drag-reference window) item-reference flavor data-ptr size-ptr 0))
  614.           data-ptr)))))
  615.  
  616. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  617. ;;
  618. ;; Easy way to work with a particular flavor of data.  Note that the 'flavor'
  619. ;; argument is an OSType.
  620. ;;
  621. (defmacro with-drag-item-flavor-data ((data-symbol window flavor item-reference) &body body)
  622.   (let ((result (gensym)))
  623.     `(let ((,result nil)
  624.            (,data-symbol (drag-item-flavor-data ,window ,flavor ,item-reference)))
  625.        (when ,data-symbol
  626.          (unwind-protect
  627.            (setf ,result (progn ,@body))
  628.            (#_DisposePtr ,data-symbol)))
  629.        ,result)))
  630.  
  631. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  632. ;;
  633. ;; Macro for iterating through all the items in a drag
  634. ;;
  635. (defmacro with-drag-items ((ref-symbol window) &body body)
  636.   (let ((item-count (gensym)))
  637.     `(let ((,item-count (drag-item-count ,window)))
  638.        (when (plusp ,item-count)
  639.          (dotimes (counter ,item-count)
  640.            (let ((,ref-symbol (drag-item-reference-number ,window (1+ counter))))
  641.              (progn ,@body)))))))
  642.  
  643. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  644. ;;
  645. ;; Setting a flavor's data (usually called from the send procedure)
  646. ;;
  647. (defmethod drag-set-item-flavor-data ((window drag-&-drop-window-mixin) (item-reference integer)
  648.                                       (flavor keyword) (item-ptr macptr) (item-size integer)
  649.                                       &optional (offset 0))
  650.   (oserr-check (#_SetDragItemFlavorData (drag-reference window) item-reference flavor item-ptr item-size offset))
  651.   t)
  652.  
  653. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  654. ;;
  655. ;; Here's where you add a flavor to a drag MCL initiates.
  656. ;;
  657. (defmethod drag-add-item-flavor ((window drag-&-drop-window-mixin) (item-reference integer)
  658.                                  (flavor keyword) (item-ptr macptr) (item-size integer)
  659.                                  &optional (flags 0))
  660.   (oserr-check (#_AddDragItemFlavor (drag-reference window) item-reference flavor item-ptr item-size flags))
  661.   t)
  662.  
  663. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  664. ;;
  665. ;; Promising a flavor...
  666. ;;
  667. (defmethod drag-promise-item-flavor ((window drag-&-drop-window-mixin) (item-reference integer)
  668.                                      (flavor keyword) &optional (flags 0))
  669.   (oserr-check (#_AddDragItemFlavor (drag-reference window) item-reference flavor (%null-ptr) 0 flags))
  670.   t)
  671.  
  672. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  673. ;;
  674. ;; Adding HFS objects as flavors
  675. ;;
  676. (defmethod drag-add-hfs-flavor ((window drag-&-drop-window-mixin) (item-reference integer) path)
  677.   (let ((result nil))
  678.     (when (or (pathnamep path) (stringp path))
  679.       (with-pstrs ((path-str (mac-namestring path)))
  680.         (rlet (($fs :FSSpec)
  681.                ($info :FInfo)
  682.                ($hfs :HFSFlavor
  683.                      :fileSpec $fs))
  684.           (oserr-check (#_FSMakeFSSpec 0 0 path-str $fs))
  685.           (oserr-check (#_FSPGetFInfo $fs $info))
  686.           (setf (pref $hfs :HFSFlavor.fdFlags) (pref $info :FInfo.fdFlags)
  687.                 (pref $hfs :HFSFlavor.fileType) (pref $info :FInfo.fdType)
  688.                 (pref $hfs :HFSFlavor.fileCreator) (pref $info :FInfo.fdCreator))
  689.           (setf result (drag-add-item-flavor window item-reference #$flavorTypeHFS
  690.                                              $hfs #.(record-length :HFSFlavor))))))
  691.     result))
  692.           
  693.  
  694. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  695. ;;
  696. ;; Promising HFS objects.
  697. ;;
  698. ;; Promising an HFS object involves a bizarre little two-step inside Drag Manager
  699. ;; callback routines.  Basically, if you promise an HFS object then you should
  700. ;; also include a 'drag-send-data method that is specialized on both your view and
  701. ;; on the flavor $PromisedHFSObject.  Your 'drag-send-data should then create the
  702. ;; file and set the flavor's data to the file's FSSpec record.  See the 'fred-example
  703. ;; example for clarification.
  704. ;;
  705. (defmethod drag-promise-hfs-flavor ((window drag-&-drop-window-mixin) (item-reference integer)
  706.                                     (file-type keyword) (file-creator keyword)
  707.                                     &key
  708.                                     (hasBeenInited nil)
  709.                                     (isStationery nil)
  710.                                     (namedLocked nil)
  711.                                     (fHasBundle nil)
  712.                                     (fIsInvisible nil)
  713.                                     (isAlias nil))
  714.   (let ((finder-flags 0))
  715.     (if hasBeenInited (incf finder-flags #X0100))       ; set the appropriate bits here
  716.     (if isStationery (incf finder-flags #X0800))
  717.     (if namedLocked (incf finder-flags #X1000))
  718.     (if fHasBundle (incf finder-flags #X2000))
  719.     (if fIsInvisible (incf finder-flags #X4000))
  720.     (if isAlias (incf finder-flags #X8000))
  721.     (rlet (($promise :PromiseHFSFlavor
  722.                      :fileType file-type
  723.                      :fileCreator file-creator
  724.                      :fdFlags finder-flags
  725.                      :promisedFlavor $PromisedHFSObject))
  726.       (drag-promise-item-flavor window item-reference $PromisedHFSObject)
  727.       (drag-add-item-flavor window item-reference #$flavorTypePromiseHFS $promise
  728.                             #.(record-length :PromiseHFSFlavor)))))
  729.  
  730. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  731. ;;
  732. ;; Same as MCL's #'find-view-containing-point but assumes a global mouse location
  733. ;;
  734. (defmethod find-view-containing-global-point ((view simple-view) (mouse integer))
  735.   (with-focused-view view
  736.     (find-view-containing-point view (global-to-local view mouse))))
  737.  
  738. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  739. ;;
  740. ;; Returns the current mouse location.  This works only during a drag.
  741. ;;
  742. (defmethod drag-mouse-location ((window drag-&-drop-window-mixin) &optional (local-p t))
  743.   (rlet ((mouse :point))
  744.     (oserr-check (#_GetDragMouse (drag-reference window) mouse (%null-ptr)))
  745.     (when local-p
  746.       (with-focused-view window
  747.         (#_GlobalToLocal mouse)))
  748.     (%get-long mouse)))
  749.  
  750. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  751. ;;
  752. ;; Returns the view the mouse is currently over.  This works only during a drag.
  753. ;;
  754. (defmethod drag-mouse-view ((window drag-&-drop-window-mixin))
  755.   (or (find-view-containing-point window (drag-mouse-location window) nil t) window))
  756.  
  757. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  758. ;;
  759. ;; Function creates the little gray areas the Drag Manager manipulates during an
  760. ;; MCL-initiated drag. The item-region argument should be a Macintosh region that
  761. ;; defines the item's area; specifying a non-nil for the optional outline-p argument
  762. ;; will cause the function to install an outline of the item's region instead.
  763. ;;
  764. (defmethod create-drag-item-bounds ((view simple-view) (item-reference integer)
  765.                                     (item-region macptr) &optional (outline-p nil))
  766.   (with-focused-view view
  767.     (let ((temp-region (copy-region item-region))
  768.           (global-point (local-to-global view (make-point 0))))
  769.       (when outline-p
  770.         (#_InsetRgn temp-region 1 1)
  771.         (#_DiffRgn item-region temp-region temp-region)
  772.         (#_OffsetRgn temp-region (point-h global-point) (point-v global-point)))
  773.       (#_UnionRgn temp-region (drag-region (view-window view)) (drag-region (view-window view)))
  774.       (with-dereferenced-handles ((rgn-ptr (drag-region (view-window view))))
  775.         (oserr-check (#_SetDragItemBounds (drag-reference (view-window view)) item-reference (pref rgn-ptr :Region.rgnBBox))))))
  776.   t)
  777.  
  778. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  779. ;;
  780. ;; Drop location stuff
  781. ;;
  782. (defmethod %resolve-alias-handle ((alias macptr))
  783.   (rlet ((fsspec :FSSpec)
  784.          (wasChanged :boolean))
  785.     (#_ResolveAlias (%null-ptr) alias fsspec wasChanged)
  786.     (let ((path (%path-from-fsspec fsspec)))
  787.       (unless (directory-pathname-p path)
  788.         (if (probe-file (format nil "~A:" path))
  789.           (setf path (make-pathname :directory (append (pathname-directory path)
  790.                                                        (list (mac-file-namestring path)))))))
  791.       path)))
  792.  
  793. ;
  794. ; A non-nil result from this function MUST be disposed via a call to #_AEDisposeDesc
  795. ;
  796. (defmethod drag-get-drop-location ((window drag-&-drop-window-mixin))
  797.   (let ((drop-location (make-record :AEDesc)))
  798.     (oserr-check (#_GetDropLocation (drag-reference window) drop-location))
  799.     (if (%null-ptr-p drop-location)
  800.       (progn
  801.         (#_AEDisposeDesc drop-location)
  802.         nil)
  803.       drop-location)))
  804.  
  805. (defmethod drag-get-drop-location-as-path ((window drag-&-drop-window-mixin))
  806.   (let ((result nil))
  807.     (with-aedescs (drop-location)
  808.       (oserr-check (#_GetDropLocation (drag-reference window) drop-location))
  809.       (when (and (not (%null-ptr-p drop-location))
  810.                  (not (%null-ptr-p (rref drop-location :AEDesc.dataHandle)))
  811.                  (eql (rref drop-location :AEDesc.descriptorType) #$rAliasType))
  812.         (setf result (%resolve-alias-handle (rref drop-location :AEDesc.dataHandle)))))
  813.     result))
  814.  
  815. #|
  816. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  817. ;;;
  818. ;;; Example with a subclass of a Fred window
  819. ;;;
  820. ;;; Dragging selections from a drag-&-drop-fred window without any modifiers
  821. ;;; performs a "normal" drag (setting up the data ahead of time and allowing the
  822. ;;; Drag Manager to cache it).  Command-dragging does not cache the data ahead
  823. ;;; of time and forces the Drag Manager to call back to MCL for it.  Option-dragging
  824. ;;; promises and HFS file instead of the selected text.
  825. ;;;
  826. ;;; drag-&-drop-fred windows can accept either TEXT data or TEXT files during drags.
  827. ;;;
  828. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  829.  
  830. (export '(fred-example))
  831.  
  832. (defun fred-example ()
  833.   (make-instance 'drag-&-drop-fred))
  834.  
  835. (defclass drag-&-drop-fred (fred-window drag-&-drop-window-mixin)
  836.   ())
  837.  
  838. (defun %drag-mouse-to-fred-position (fred-window)
  839.   ; maps drag mouse location to fred buffer position
  840.   (let ((pos (drag-mouse-location fred-window)))
  841.     (fred-point-position fred-window pos)))
  842.  
  843. (defmethod drag-tracking-enter-view ((w drag-&-drop-fred))
  844.   (unless (drag-within-sender-view-p w)
  845.     (view-drag-hilite w t #@(0 0) (make-point (- (point-h (view-size w)) 15)
  846.                                                 (- (point-v (view-size w)) 15)))))
  847.  
  848. (defmethod drag-tracking-leave-view ((w drag-&-drop-fred))
  849.   (view-drag-hilite w nil))
  850.  
  851. (defmethod drag-receive-drag ((w drag-&-drop-fred))
  852.   (unless (drag-within-sender-view-p w)       ; don't allow drag&drop within same MCL window
  853.     (let ((pos (%drag-mouse-to-fred-position w))        ; Fred insertion point for drop
  854.           (text-flavor :|TEXT|)
  855.           (some-item-used-p nil))
  856.       (with-drag-items (item-reference w)
  857.         (cond ((drag-item-flavor-exists-p w text-flavor item-reference)
  858.                (setf some-item-used-p t)
  859.                (with-drag-item-flavor-data (text-ptr w text-flavor item-reference)
  860.                  (let* ((size (#_GetPtrSize text-ptr))
  861.                         (new-string (make-string size)))
  862.                    (without-interrupts
  863.                     (dotimes (counter size)
  864.                       (setf (elt new-string counter) (code-char (%get-byte text-ptr counter))))
  865.                     (ed-insert-with-undo w new-string pos))
  866.                    (invalidate-view w))))
  867.               ((drag-item-flavor-exists-p w #$flavorTypeHFS item-reference)
  868.                (with-drag-item-flavor-data (fsspec w #$flavorTypeHFS item-reference)
  869.                  (when (eql (pref fsspec :HFSFlavor.fileType) text-flavor)
  870.                    (setf some-item-used-p t)
  871.                    (with-cursor *watch-cursor*
  872.                      (let ((path (%path-from-fsspec (pref fsspec :HFSFlavor.fileSpec))))
  873.                        (buffer-insert-file (fred-buffer w) path pos)
  874.                        (invalidate-view w))))))))
  875.       some-item-used-p)))
  876.  
  877. (defmethod drag-selection-p ((w drag-&-drop-fred) mouse-position)
  878.   (with-focused-view w
  879.     (point-in-region-p (uvref (frec w) 27)      ; hilite region within a Fred window
  880.                        (global-to-local w mouse-position))))
  881.  
  882. (defmethod drag-begin-drag ((w drag-&-drop-fred) eventrecord)
  883.   (with-cursor *arrow-cursor*
  884.     (with-new-drag w eventrecord
  885.       (multiple-value-bind (start end) (selection-range w)
  886.         (let ((size (min (- end start) 30000))
  887.               ($Buffer (#_NewPtr 0)))
  888.           (unwind-protect
  889.             (progn
  890.               (cond ((option-key-p)
  891.                      (drag-promise-hfs-flavor w 1 :|TEXT| :|CCL2|))
  892.                     ((command-key-p)
  893.                      (drag-promise-item-flavor w 1 :|TEXT|))
  894.                     (t
  895.                      (#_SetPtrSize $Buffer size)
  896.                      (without-interrupts
  897.                       (dotimes (counter size)
  898.                         (%put-byte $Buffer (char-code (buffer-char (fred-buffer w) (+ start counter))) counter)))
  899.                      (drag-add-item-flavor w 1 :|TEXT| $Buffer size)))
  900.               (create-drag-item-bounds w 1 (uvref (frec w) 27) t))
  901.             (#_DisposePtr $Buffer))
  902.           t)))))
  903.  
  904. (defmethod drag-send-data ((w drag-&-drop-fred) (item-reference integer)
  905.                            (flavor (eql :|TEXT|)))
  906.   (%stack-block (($Buffer 30000))
  907.     (multiple-value-bind (start end) (selection-range w)
  908.       (let ((size (min (- end start) 30000)))
  909.         (without-interrupts
  910.          (dotimes (counter size)
  911.            (%put-byte $Buffer (char-code (buffer-char (fred-buffer w) (+ start counter))) counter)))
  912.         (drag-set-item-flavor-data w item-reference :|TEXT| $Buffer size)))
  913.     t))
  914.  
  915. (defmethod drag-send-data ((w drag-&-drop-fred) (item-reference integer)
  916.                            (flavor (eql $PromisedHFSObject)))
  917.   (with-cursor *watch-cursor*
  918.     (multiple-value-bind (start end) (selection-range w)
  919.       (let ((temp (make-buffer))
  920.             (drop-folder (drag-get-drop-location-as-path w))
  921.             (title (window-title w))
  922.             (counter 1)
  923.             (path nil))
  924.         (if (> (length title) 28)
  925.           (setf title (format nil "~AÉ" (subseq title 0 28))))
  926.         (flet ((unique-pathname ()
  927.                  (make-pathname :directory (pathname-directory drop-folder)
  928.                                 :name (format nil "~A ~D" title counter)
  929.                                 :type "lisp")))
  930.           (when drop-folder
  931.             (setf path (unique-pathname))
  932.             (loop while (probe-file path)
  933.                   do (incf counter)
  934.                   do (setf path (unique-pathname)))
  935.             (buffer-insert temp (buffer-substring (fred-buffer w) start end))
  936.             (buffer-write-file temp path :if-exists :append)
  937.             (set-mac-file-type path :|TEXT|)
  938.             (set-mac-file-creator path :|CCL2|)
  939.             (rlet (($fs :FSSpec))
  940.               (with-pstrs ((path-str (mac-namestring path)))
  941.                 (oserr-check (#_FSMakeFSSpec 0 0 path-str $fs))
  942.                 (drag-set-item-flavor-data w item-reference $PromisedHFSObject $fs #.(record-length :FSSpec))))
  943.             t))))))
  944.         
  945.  
  946.  
  947. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  948. ;;;
  949. ;;; Example using multiple editable-text-dialog-items
  950. ;;;
  951. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  952.  
  953. (export '(text-item-example))
  954.  
  955. (defun text-item-example ()
  956.   (make-instance 'drag-window
  957.     :window-type :document
  958.     :view-position #@(3 40)
  959.     :view-size #@(336 356)
  960.     :view-font '("Geneva" 9 :srcor :plain)
  961.     :view-subviews
  962.     (list (make-dialog-item 'drop-text-field
  963.                             #@(25 72)
  964.                             #@(80 177)
  965.                             ""
  966.                             'nil
  967.                             :wrap-p t
  968.                             :view-font '("Geneva" 9 :srcor :plain)
  969.                             :allow-returns t)
  970.           (make-dialog-item 'drop-text-field
  971.                             #@(125 72)
  972.                             #@(80 177)
  973.                             ""
  974.                             'nil
  975.                             :wrap-p t
  976.                             :view-font '("Geneva" 9 :srcor :plain)
  977.                             :allow-returns t))))
  978.  
  979. (defclass drag-window (color-dialog drag-&-drop-window-mixin)
  980.   ())
  981.  
  982. (defclass drop-text-field (editable-text-dialog-item)
  983.   ())
  984.  
  985. (defmethod drag-tracking-enter-view ((view drop-text-field))
  986.   (unless (drag-within-sender-view-p (view-window view))
  987.     (view-drag-hilite view t)))
  988.  
  989. (defmethod drag-tracking-leave-view ((view drop-text-field))
  990.   (view-drag-hilite view nil))
  991.  
  992. (defun %drag-mouse-to-fred-position (fred-window)
  993.   ; maps drag mouse location to fred buffer position
  994.   (let ((pos (drag-mouse-location fred-window)))
  995.     (fred-point-position fred-window pos)))
  996.  
  997. (defmethod drag-receive-drag ((view drop-text-field))
  998.   (unless (drag-within-sender-view-p view)
  999.     (let ((my-window (view-window view))
  1000.           (text-flavor :|TEXT|)
  1001.           (some-item-used-p nil))
  1002.       (with-drag-items (item-reference my-window)
  1003.         (cond ((drag-item-flavor-exists-p my-window text-flavor item-reference)
  1004.                (setf some-item-used-p t)
  1005.                (with-drag-item-flavor-data (text-ptr my-window text-flavor item-reference)
  1006.                  (let* ((size (#_GetPtrSize text-ptr))
  1007.                         (new-string (make-string size)))
  1008.                    (without-interrupts
  1009.                     (dotimes (counter size)
  1010.                       (setf (elt new-string counter) (code-char (%get-byte text-ptr counter))))
  1011.                     (ed-insert-with-undo view new-string))
  1012.                    (set-current-key-handler my-window view nil)
  1013.                    (invalidate-view view))))
  1014.               ((drag-item-flavor-exists-p my-window #$flavorTypeHFS item-reference)
  1015.                (with-drag-item-flavor-data (fsspec my-window #$flavorTypeHFS item-reference)
  1016.                  (when (eql (pref fsspec :HFSFlavor.fileType) text-flavor)
  1017.                    (setf some-item-used-p t)
  1018.                    (with-cursor *watch-cursor*
  1019.                      (let ((path (%path-from-fsspec (pref fsspec :HFSFlavor.fileSpec)))
  1020.                            (buffer (make-buffer)))
  1021.                        (buffer-insert-file buffer path)
  1022.                        (ed-insert-with-undo view (buffer-substring buffer 0 t))
  1023.                        (set-current-key-handler my-window view nil)
  1024.                        (invalidate-view view))))))
  1025.               ))
  1026.       some-item-used-p)))
  1027.  
  1028. (defmethod drag-selection-p ((view drop-text-field) mouse-position)
  1029.   (with-focused-view view
  1030.     (point-in-region-p (uvref (frec view) 27)      ; hilite region within a Fred item
  1031.                        (global-to-local view mouse-position))))
  1032.  
  1033. (defmethod drag-begin-drag ((view drop-text-field) eventrecord)
  1034.   (with-cursor *arrow-cursor*
  1035.     (with-new-drag (view-window view) eventrecord
  1036.       (multiple-value-bind (start end) (selection-range view)
  1037.         (let ((size (min (- end start) 30000)))
  1038.           (%stack-block (($Buffer 30000))
  1039.             (without-interrupts
  1040.              (dotimes (counter size)
  1041.                (%put-byte $Buffer (char-code (buffer-char (fred-buffer view) (+ start counter))) counter)))
  1042.             (drag-add-item-flavor (view-window view) 1 :|TEXT| $Buffer size)
  1043.             (create-drag-item-bounds view 1 (uvref (frec view) 27) t)))))))
  1044. |#